 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.actions;

 import java.io.File ;
 import java.lang.reflect.InvocationTargetException ;
 import java.net.URI ;
 import com.ibm.icu.text.MessageFormat;
 import java.util.ArrayList ;
 import java.util.Arrays ;
 import java.util.List ;

 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.core.filesystem.EFS;
 import org.eclipse.core.filesystem.IFileInfo;
 import org.eclipse.core.filesystem.IFileStore;
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IWorkspace;
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.resources.WorkspaceJob;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.MultiStatus;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.SubProgressMonitor;
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.IInputValidator;
 import org.eclipse.jface.dialogs.InputDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.window.Window;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.dialogs.IOverwriteQuery;
 import org.eclipse.ui.ide.undo.AbstractWorkspaceOperation;
 import org.eclipse.ui.ide.undo.CopyResourcesOperation;
 import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
 import org.eclipse.ui.internal.ide.StatusUtil;
 import org.eclipse.ui.internal.ide.dialogs.IDEResourceInfoUtils;
 import org.eclipse.ui.wizards.datatransfer.FileStoreStructureProvider;
 import org.eclipse.ui.wizards.datatransfer.ImportOperation;

 /**
  * Perform the copy of file and folder resources from the clipboard when paste
  * action is invoked.
  * <p>
  * This class may be instantiated; it is not intended to be subclassed.
  * </p>
  */
 public class CopyFilesAndFoldersOperation {

     /**
      * Status containing the errors detected when running the operation or
      * <code>null</code> if no errors detected.
      */
     private MultiStatus errorStatus;

     /**
      * The parent shell used to show any dialogs.
      */
     private Shell messageShell;

     /**
      * Whether or not the copy has been canceled by the user.
      */
     private boolean canceled = false;

     /**
      * Overwrite all flag.
      */
     private boolean alwaysOverwrite = false;

     private String [] modelProviderIds;

     /**
      * Returns a new name for a copy of the resource at the given path in the
      * given workspace. This name is determined automatically.
      *
      * @param originalName
      * the full path of the resource
      * @param workspace
      * the workspace
      * @return the new full path for the copy
      */
     static IPath getAutoNewNameFor(IPath originalName, IWorkspace workspace) {
         int counter = 1;
         String resourceName = originalName.lastSegment();
         IPath leadupSegment = originalName.removeLastSegments(1);

         while (true) {
             String nameSegment;

             if (counter > 1) {
                 nameSegment = NLS
                         .bind(
                                 IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyNameTwoArgs,
                                 new Integer (counter), resourceName);
             } else {
                 nameSegment = NLS
                         .bind(
                                 IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyNameOneArg,
                                 resourceName);
             }

             IPath pathToTry = leadupSegment.append(nameSegment);

             if (!workspace.getRoot().exists(pathToTry)) {
                 return pathToTry;
             }

             counter++;
         }
     }

     /**
      * Creates a new operation initialized with a shell.
      *
      * @param shell
      * parent shell for error dialogs
      */
     public CopyFilesAndFoldersOperation(Shell shell) {
         messageShell = shell;
     }

     /**
      * Returns whether this operation is able to perform on-the-fly
      * auto-renaming of resources with name collisions.
      *
      * @return <code>true</code> if auto-rename is supported, and
      * <code>false</code> otherwise
      */
     protected boolean canPerformAutoRename() {
         return true;
     }

     /**
      * Returns the message for querying deep copy/move of a linked resource.
      *
      * @param source
      * resource the query is made for
      * @return the deep query message
      */
     protected String getDeepCheckQuestion(IResource source) {
         return NLS
                 .bind(
                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_deepCopyQuestion,
                         source.getFullPath().makeRelative());
     }

     /**
      * Checks whether the infos exist.
      *
      * @param stores
      * the file infos to test
      * @return Multi status with one error message for each missing file.
      */
     IStatus checkExist(IFileStore[] stores) {
         MultiStatus multiStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
                 IStatus.OK, getProblemsMessage(), null);

         for (int i = 0; i < stores.length; i++) {
             if (stores[i].fetchInfo().exists() == false) {
                 String message = NLS
                         .bind(
                                 IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
                                 stores[i].getName());
                 IStatus status = new Status(IStatus.ERROR,
                         PlatformUI.PLUGIN_ID, IStatus.OK, message, null);
                 multiStatus.add(status);
             }
         }
         return multiStatus;
     }

     /**
      * Checks whether the resources with the given names exist.
      *
      * @param resources
      * IResources to checl
      * @return Multi status with one error message for each missing file.
      */
     IStatus checkExist(IResource[] resources) {
         MultiStatus multiStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
                 IStatus.OK, getProblemsMessage(), null);

         for (int i = 0; i < resources.length; i++) {
             IResource resource = resources[i];
             if (resource != null) {
                 URI location = resource.getLocationURI();
                 String message = null;
                 if (location != null) {
                     IFileInfo info = IDEResourceInfoUtils.getFileInfo(location);
                     if (info == null || info.exists() == false) {
                         if (resource.isLinked()) {
                             message = NLS
                                     .bind(
                                             IDEWorkbenchMessages.CopyFilesAndFoldersOperation_missingLinkTarget,
                                             resource.getName());
                         } else {
                             message = NLS
                                     .bind(
                                             IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
                                             resource.getName());
                         }
                     }
                 }
                 if (message != null) {
                     IStatus status = new Status(IStatus.ERROR,
                             PlatformUI.PLUGIN_ID, IStatus.OK, message, null);
                     multiStatus.add(status);
                 }
             }
         }
         return multiStatus;
     }

     /**
      * Check if the user wishes to overwrite the supplied resource or all
      * resources.
      *
      * @param source
      * the source resource
      * @param destination
      * the resource to be overwritten
      * @return one of IDialogConstants.YES_ID, IDialogConstants.YES_TO_ALL_ID,
      * IDialogConstants.NO_ID, IDialogConstants.CANCEL_ID indicating
      * whether the current resource or all resources can be overwritten,
      * or if the operation should be canceled.
      */
     private int checkOverwrite(final IResource source,
             final IResource destination) {
         final int[] result = new int[1];

         // Dialogs need to be created and opened in the UI thread
 Runnable query = new Runnable () {
             public void run() {
                 String message;
                 int resultId[] = { IDialogConstants.YES_ID,
                         IDialogConstants.YES_TO_ALL_ID, IDialogConstants.NO_ID,
                         IDialogConstants.CANCEL_ID };
                 String labels[] = new String [] { IDialogConstants.YES_LABEL,
                         IDialogConstants.YES_TO_ALL_LABEL,
                         IDialogConstants.NO_LABEL,
                         IDialogConstants.CANCEL_LABEL };

                 if (destination.getType() == IResource.FOLDER) {
                     if (homogenousResources(source, destination)) {
                         message = NLS
                                 .bind(
                                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteMergeQuestion,
                                         destination.getFullPath()
                                                 .makeRelative());
                     } else {
                         if (destination.isLinked()) {
                             message = NLS
                                     .bind(
                                             IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteNoMergeLinkQuestion,
                                             destination.getFullPath()
                                                     .makeRelative());
                         } else {
                             message = NLS
                                     .bind(
                                             IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteNoMergeNoLinkQuestion,
                                             destination.getFullPath()
                                                     .makeRelative());
                         }
                         resultId = new int[] { IDialogConstants.YES_ID,
                                 IDialogConstants.NO_ID,
                                 IDialogConstants.CANCEL_ID };
                         labels = new String [] { IDialogConstants.YES_LABEL,
                                 IDialogConstants.NO_LABEL,
                                 IDialogConstants.CANCEL_LABEL };
                     }
                 } else {
                     String [] bindings = new String [] {
                             IDEResourceInfoUtils.getLocationText(destination),
                             IDEResourceInfoUtils
                                     .getDateStringValue(destination),
                             IDEResourceInfoUtils.getLocationText(source),
                             IDEResourceInfoUtils.getDateStringValue(source) };
                     message = NLS
                             .bind(
                                     IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteWithDetailsQuestion,
                                     bindings);
                 }
                 MessageDialog dialog = new MessageDialog(
                         messageShell,
                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceExists,
                         null, message, MessageDialog.QUESTION, labels, 0);
                 dialog.open();
                 if (dialog.getReturnCode() == SWT.DEFAULT) {
                     // A window close returns SWT.DEFAULT, which has to be
 // mapped to a cancel
 result[0] = IDialogConstants.CANCEL_ID;
                 } else {
                     result[0] = resultId[dialog.getReturnCode()];
                 }
             }
         };
         messageShell.getDisplay().syncExec(query);
         return result[0];
     }

     /**
      * Recursively collects existing files in the specified destination path.
      *
      * @param destinationPath
      * destination path to check for existing files
      * @param copyResources
      * resources that may exist in the destination
      * @param existing
      * holds the collected existing files
      */
     private void collectExistingReadonlyFiles(IPath destinationPath,
             IResource[] copyResources, ArrayList existing) {
         IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();

         for (int i = 0; i < copyResources.length; i++) {
             IResource source = copyResources[i];
             IPath newDestinationPath = destinationPath.append(source.getName());
             IResource newDestination = workspaceRoot
                     .findMember(newDestinationPath);
             IFolder folder;

             if (newDestination == null) {
                 continue;
             }
             folder = getFolder(newDestination);
             if (folder != null) {
                 IFolder sourceFolder = getFolder(source);

                 if (sourceFolder != null) {
                     try {
                         collectExistingReadonlyFiles(newDestinationPath,
                                 sourceFolder.members(), existing);
                     } catch (CoreException exception) {
                         recordError(exception);
                     }
                 }
             } else {
                 IFile file = getFile(newDestination);

                 if (file != null) {
                     if (file.isReadOnly()) {
                         existing.add(file);
                     }
                     if (getValidateConflictSource()) {
                         IFile sourceFile = getFile(source);
                         if (sourceFile != null) {
                             existing.add(sourceFile);
                         }
                     }
                 }
             }
         }
     }

     /**
      * Copies the resources to the given destination. This method is called
      * recursively to merge folders during folder copy.
      *
      * @param resources
      * the resources to copy
      * @param destination
      * destination to which resources will be copied
      * @param subMonitor
      * a progress monitor for showing progress and for cancelation
      *
      * @deprecated As of 3.3, the work is performed in the undoable operation
      * created in
      * {@link #getUndoableCopyOrMoveOperation(IResource[], IPath)}
      */
     protected void copy(IResource[] resources, IPath destination,
             IProgressMonitor subMonitor) throws CoreException {

         subMonitor
                 .beginTask(
                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_CopyResourcesTask,
                         resources.length);

         for (int i = 0; i < resources.length; i++) {
             IResource source = resources[i];
             IPath destinationPath = destination.append(source.getName());
             IWorkspace workspace = source.getWorkspace();
             IWorkspaceRoot workspaceRoot = workspace.getRoot();
             IResource existing = workspaceRoot.findMember(destinationPath);
             if (source.getType() == IResource.FOLDER && existing != null) {
                 // the resource is a folder and it exists in the destination,
 // copy the
 // children of the folder.
 if (homogenousResources(source, existing)) {
                     IResource[] children = ((IContainer) source).members();
                     copy(children, destinationPath, new SubProgressMonitor(
                             subMonitor, 1));
                 } else {
                     // delete the destination folder, copying a linked folder
 // over an unlinked one or vice versa. Fixes bug 28772.
 delete(existing, new SubProgressMonitor(subMonitor, 0));
                     source.copy(destinationPath, IResource.SHALLOW,
                             new SubProgressMonitor(subMonitor, 1));
                 }
             } else {
                 if (existing != null) {
                     if (homogenousResources(source, existing)) {
                         copyExisting(source, existing, new SubProgressMonitor(
                                 subMonitor, 1));
                     } else {
                         // Copying a linked resource over unlinked or vice
 // versa.
 // Can't use setContents here. Fixes bug 28772.
 delete(existing, new SubProgressMonitor(subMonitor, 0));
                         source.copy(destinationPath, IResource.SHALLOW,
                                 new SubProgressMonitor(subMonitor, 1));
                     }
                 } else {
                     source.copy(destinationPath, IResource.SHALLOW,
                             new SubProgressMonitor(subMonitor, 1));

                 }

                 if (subMonitor.isCanceled()) {
                     throw new OperationCanceledException();
                 }
             }
         }
     }

     /**
      * Sets the content of the existing file to the source file content.
      *
      * @param source
      * source file to copy
      * @param existing
      * existing file to set the source content in
      * @param subMonitor
      * a progress monitor for showing progress and for cancelation
      * @throws CoreException
      * setContents failed
      */
     private void copyExisting(IResource source, IResource existing,
             IProgressMonitor subMonitor) throws CoreException {
         IFile existingFile = getFile(existing);

         if (existingFile != null) {
             IFile sourceFile = getFile(source);

             if (sourceFile != null) {
                 existingFile.setContents(sourceFile.getContents(),
                         IResource.KEEP_HISTORY, new SubProgressMonitor(
                                 subMonitor, 0));
             }
         }
     }

     /**
      * Copies the given resources to the destination. The current Thread is
      * halted while the resources are copied using a WorkspaceModifyOperation.
      * This method should be called from the UIThread.
      *
      * @param resources
      * the resources to copy
      * @param destination
      * destination to which resources will be copied
      * @return IResource[] the resulting {@link IResource}[]
      * @see WorkspaceModifyOperation
      * @see Display#getThread()
      * @see Thread#currentThread()
      */
     public IResource[] copyResources(final IResource[] resources,
             IContainer destination) {
         return copyResources(resources, destination, true, null);
     }

     /**
      * Copies the given resources to the destination in the current Thread
      * without forking a new Thread or blocking using a
      * WorkspaceModifyOperation. It recommended that this method only be called
      * from a {@link WorkspaceJob} to avoid possible deadlock.
      *
      * @param resources
      * the resources to copy
      * @param destination
      * destination to which resources will be copied
      * @param monitor
      * the monitor that information will be sent to.
      * @return IResource[] the resulting {@link IResource}[]
      * @see WorkspaceModifyOperation
      * @see WorkspaceJob
      * @since 3.2
      */
     public IResource[] copyResourcesInCurrentThread(
             final IResource[] resources, IContainer destination,
             IProgressMonitor monitor) {
         return copyResources(resources, destination, false, monitor);
     }

     /**
      * Copies the given resources to the destination.
      *
      * @param resources
      * the resources to copy
      * @param destination
      * destination to which resources will be copied
      * @return IResource[] the resulting {@link IResource}[]
      */
     private IResource[] copyResources(final IResource[] resources,
             IContainer destination, boolean fork, IProgressMonitor monitor) {
         final IPath destinationPath = destination.getFullPath();
         final IResource[][] copiedResources = new IResource[1][0];

         // test resources for existence separate from validate API.
 // Validate is performance critical and resource exists
 // check is potentially slow. Fixes bugs 16129/28602.
 IStatus resourceStatus = checkExist(resources);
         if (resourceStatus.getSeverity() != IStatus.OK) {
             displayError(resourceStatus);
             return copiedResources[0];
         }
         String errorMsg = validateDestination(destination, resources);
         if (errorMsg != null) {
             displayError(errorMsg);
             return copiedResources[0];
         }

         IRunnableWithProgress op = new IRunnableWithProgress() {
             public void run(IProgressMonitor monitor) {
                 copyResources(resources, destinationPath, copiedResources,
                         monitor);
             }
         };

         try {
             PlatformUI.getWorkbench().getProgressService().run(fork, true, op);
         } catch (InterruptedException e) {
             return copiedResources[0];
         } catch (InvocationTargetException e) {
             display(e);
         }

         // If errors occurred, open an Error dialog
 if (errorStatus != null) {
             displayError(errorStatus);
             errorStatus = null;
         }

         return copiedResources[0];
     }

     /**
      * Return whether the operation is a move or a copy
      *
      * @return whether the operation is a move or a copy
      * @since 3.2
      */
     protected boolean isMove() {
         return false;
     }

     private void display(InvocationTargetException e) {
         // CoreExceptions are collected above, but unexpected runtime
 // exceptions and errors may still occur.
 IDEWorkbenchPlugin.getDefault().getLog().log(
                 StatusUtil.newStatus(IStatus.ERROR, MessageFormat.format(
                         "Exception in {0}.performCopy(): {1}", //$NON-NLS-1$
 new Object [] { getClass().getName(),
                                 e.getTargetException() }), null));
         displayError(NLS
                 .bind(
                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
                         e.getTargetException().getMessage()));
     }

     /**
      * Copies the given URIS and folders to the destination. The current Thread
      * is halted while the resources are copied using a
      * WorkspaceModifyOperation. This method should be called from the UI
      * Thread.
      *
      * @param uris
      * the URIs to copy
      * @param destination
      * destination to which files will be copied
      * @see WorkspaceModifyOperation
      * @see Display#getThread()
      * @see Thread#currentThread()
      * @since 3.2
      */
     public void copyFiles(URI [] uris, IContainer destination) {
         IFileStore[] stores = buildFileStores(uris);
         if (stores == null) {
             return;
         }

         copyFileStores(destination, stores, true, null);
     }

     /**
      * Copies the given files and folders to the destination without forking a
      * new Thread or blocking using a WorkspaceModifyOperation. It is
      * recommended that this method only be called from a {@link WorkspaceJob}
      * to avoid possible deadlock.
      *
      * @param uris
      * the URIs to copy
      * @param destination
      * destination to which URIS will be copied
      * @param monitor
      * the monitor that information will be sent to.
      * @see WorkspaceModifyOperation
      * @see WorkspaceJob
      * @since 3.2
      */
     public void copyFilesInCurrentThread(URI [] uris, IContainer destination,
             IProgressMonitor monitor) {
         IFileStore[] stores = buildFileStores(uris);
         if (stores == null) {
             return;
         }

         copyFileStores(destination, stores, false, monitor);
     }

     /**
      * Build the collection of fileStores that map to fileNames. If any of them
      * cannot be found then match then return <code>null</code>.
      *
      * @param uris
      * @return IFileStore[]
      */
     private IFileStore[] buildFileStores(URI [] uris) {
         IFileStore[] stores = new IFileStore[uris.length];
         for (int i = 0; i < uris.length; i++) {
             IFileStore store;
             try {
                 store = EFS.getStore(uris[i]);
             } catch (CoreException e) {
                 IDEWorkbenchPlugin.log(e.getMessage(), e);
                 reportFileInfoNotFound(uris[i].toString());
                 return null;
             }
             if (store == null) {
                 reportFileInfoNotFound(uris[i].toString());
                 return null;
             }
             stores[i] = store;
         }
         return stores;

     }

     /**
      * Copies the given files and folders to the destination. The current Thread
      * is halted while the resources are copied using a
      * WorkspaceModifyOperation. This method should be called from the UI
      * Thread.
      *
      * @param fileNames
      * names of the files to copy
      * @param destination
      * destination to which files will be copied
      * @see WorkspaceModifyOperation
      * @see Display#getThread()
      * @see Thread#currentThread()
      * @since 3.2
      */
     public void copyFiles(final String [] fileNames, IContainer destination) {
         IFileStore[] stores = buildFileStores(fileNames);
         if (stores == null) {
             return;
         }

         copyFileStores(destination, stores, true, null);
     }

     /**
      * Copies the given files and folders to the destination without forking a
      * new Thread or blocking using a WorkspaceModifyOperation. It is
      * recommended that this method only be called from a {@link WorkspaceJob}
      * to avoid possible deadlock.
      *
      * @param fileNames
      * names of the files to copy
      * @param destination
      * destination to which files will be copied
      * @param monitor
      * the monitor that information will be sent to.
      * @see WorkspaceModifyOperation
      * @see WorkspaceJob
      * @since 3.2
      */
     public void copyFilesInCurrentThread(final String [] fileNames,
             IContainer destination, IProgressMonitor monitor) {
         IFileStore[] stores = buildFileStores(fileNames);
         if (stores == null) {
             return;
         }

         copyFileStores(destination, stores, false, monitor);
     }

     /**
      * Build the collection of fileStores that map to fileNames. If any of them
      * cannot be found then match then return null.
      *
      * @param fileNames
      * @return IFileStore[]
      */
     private IFileStore[] buildFileStores(final String [] fileNames) {
         IFileStore[] stores = new IFileStore[fileNames.length];
         for (int i = 0; i < fileNames.length; i++) {
             IFileStore store = IDEResourceInfoUtils.getFileStore(fileNames[i]);
             if (store == null) {
                 reportFileInfoNotFound(fileNames[i]);
                 return null;
             }
             stores[i] = store;
         }
         return stores;
     }

     /**
      * Report that a file info could not be found.
      *
      * @param fileName
      */
     private void reportFileInfoNotFound(final String fileName) {

         messageShell.getDisplay().syncExec(new Runnable () {
             public void run() {
                 ErrorDialog
                         .openError(
                                 messageShell,
                                 getProblemsTitle(),
                                 NLS
                                         .bind(
                                                 IDEWorkbenchMessages.CopyFilesAndFoldersOperation_infoNotFound,
                                                 fileName), null);
             }
         });
     }

     /**
      * Copies the given files and folders to the destination.
      *
      * @param stores
      * the file stores to copy
      * @param destination
      * destination to which files will be copied
      */
     private void copyFileStores(IContainer destination,
             final IFileStore[] stores, boolean fork, IProgressMonitor monitor) {
         // test files for existence separate from validate API
 // because an external file may not exist until the copy actually
 // takes place (e.g., WinZip contents).
 IStatus fileStatus = checkExist(stores);
         if (fileStatus.getSeverity() != IStatus.OK) {
             displayError(fileStatus);
             return;
         }
         String errorMsg = validateImportDestinationInternal(destination, stores);
         if (errorMsg != null) {
             displayError(errorMsg);
             return;
         }
         final IPath destinationPath = destination.getFullPath();

         if (fork) {
             WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
                 public void execute(IProgressMonitor monitor) {
                     copyFileStores(stores, destinationPath, monitor);
                 }
             };
             try {
                 PlatformUI.getWorkbench().getProgressService().run(true, true,
                         op);
             } catch (InterruptedException e) {
                 return;
             } catch (InvocationTargetException exception) {
                 display(exception);
             }
         } else {
             copyFileStores(stores, destinationPath, monitor);
         }

         // If errors occurred, open an Error dialog
 if (errorStatus != null) {
             displayError(errorStatus);
             errorStatus = null;
         }
     }

     /**
      * Display the supplied status in an error dialog.
      *
      * @param status
      * The status to display
      */
     private void displayError(final IStatus status) {
         messageShell.getDisplay().syncExec(new Runnable () {
             public void run() {
                 ErrorDialog.openError(messageShell, getProblemsTitle(), null,
                         status);
             }
         });
     }

     /**
      * Creates a file or folder handle for the source resource as if it were to
      * be created in the destination container.
      *
      * @param destination
      * destination container
      * @param source
      * source resource
      * @return IResource file or folder handle, depending on the source type.
      */
     IResource createLinkedResourceHandle(IContainer destination,
             IResource source) {
         IWorkspace workspace = destination.getWorkspace();
         IWorkspaceRoot workspaceRoot = workspace.getRoot();
         IPath linkPath = destination.getFullPath().append(source.getName());
         IResource linkHandle;

         if (source.getType() == IResource.FOLDER) {
             linkHandle = workspaceRoot.getFolder(linkPath);
         } else {
             linkHandle = workspaceRoot.getFile(linkPath);
         }
         return linkHandle;
     }

     /**
      * Removes the given resource from the workspace.
      *
      * @param resource
      * resource to remove from the workspace
      * @param monitor
      * a progress monitor for showing progress and for cancelation
      * @return true the resource was deleted successfully false the resource was
      * not deleted because a CoreException occurred
      */
     boolean delete(IResource resource, IProgressMonitor monitor) {
         boolean force = false; // don't force deletion of out-of-sync resources

         if (resource.getType() == IResource.PROJECT) {
             // if it's a project, ask whether content should be deleted too
 IProject project = (IProject) resource;
             try {
                 project.delete(true, force, monitor);
             } catch (CoreException e) {
                 recordError(e); // log error
 return false;
             }
         } else {
             // if it's not a project, just delete it
 int flags = IResource.KEEP_HISTORY;
             if (force) {
                 flags = flags | IResource.FORCE;
             }
             try {
                 resource.delete(flags, monitor);
             } catch (CoreException e) {
                 recordError(e); // log error
 return false;
             }
         }
         return true;
     }

     /**
      * Opens an error dialog to display the given message.
      *
      * @param message
      * the error message to show
      */
     private void displayError(final String message) {
         messageShell.getDisplay().syncExec(new Runnable () {
             public void run() {
                 MessageDialog.openError(messageShell, getProblemsTitle(),
                         message);
             }
         });
     }

     /**
      * Returns the resource either casted to or adapted to an IFile.
      *
      * @param resource
      * resource to cast/adapt
      * @return the resource either casted to or adapted to an IFile.
      * <code>null</code> if the resource does not adapt to IFile
      */
     protected IFile getFile(IResource resource) {
         if (resource instanceof IFile) {
             return (IFile) resource;
         }
         return (IFile) ((IAdaptable) resource).getAdapter(IFile.class);
     }

     /**
      * Returns java.io.File objects for the given file names.
      *
      * @param fileNames
      * files to return File object for.
      * @return java.io.File objects for the given file names.
      * @deprecated As of 3.3, this method is no longer in use anywhere in this
      * class and is only provided for backwards compatability with
      * subclasses of the receiver.
      */
     protected File [] getFiles(String [] fileNames) {
         File [] files = new File [fileNames.length];

         for (int i = 0; i < fileNames.length; i++) {
             files[i] = new File (fileNames[i]);
         }
         return files;
     }

     /**
      * Returns the resource either casted to or adapted to an IFolder.
      *
      * @param resource
      * resource to cast/adapt
      * @return the resource either casted to or adapted to an IFolder.
      * <code>null</code> if the resource does not adapt to IFolder
      */
     protected IFolder getFolder(IResource resource) {
         if (resource instanceof IFolder) {
             return (IFolder) resource;
         }
         return (IFolder) ((IAdaptable) resource).getAdapter(IFolder.class);
     }

     /**
      * Returns a new name for a copy of the resource at the given path in the
      * given workspace.
      *
      * @param originalName
      * the full path of the resource
      * @param workspace
      * the workspace
      * @return the new full path for the copy, or <code>null</code> if the
      * resource should not be copied
      */
     private IPath getNewNameFor(final IPath originalName,
             final IWorkspace workspace) {
         final IResource resource = workspace.getRoot().findMember(originalName);
         final IPath prefix = resource.getFullPath().removeLastSegments(1);
         final String returnValue[] = { "" }; //$NON-NLS-1$

         messageShell.getDisplay().syncExec(new Runnable () {
             public void run() {
                 IInputValidator validator = new IInputValidator() {
                     public String isValid(String string) {
                         if (resource.getName().equals(string)) {
                             return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameMustBeDifferent;
                         }
                         IStatus status = workspace.validateName(string,
                                 resource.getType());
                         if (!status.isOK()) {
                             return status.getMessage();
                         }
                         if (workspace.getRoot().exists(prefix.append(string))) {
                             return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameExists;
                         }
                         return null;
                     }
                 };

                 InputDialog dialog = new InputDialog(
                         messageShell,
                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_inputDialogTitle,
                         NLS
                                 .bind(
                                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_inputDialogMessage,
                                        resource.getName()), getAutoNewNameFor(
                                originalName, workspace).lastSegment()
                                .toString(), validator);
                dialog.setBlockOnOpen(true);
                dialog.open();
                if (dialog.getReturnCode() == Window.CANCEL) {
                    returnValue[0] = null;
                } else {
                    returnValue[0] = dialog.getValue();
                }
            }
        });
        if (returnValue[0] == null) {
            throw new OperationCanceledException();
        }
        return prefix.append(returnValue[0]);
    }

    /**
     * Returns the task title for this operation's progress dialog.
     *
     * @return the task title
     */
    protected String getOperationTitle() {
        return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_operationTitle;
    }

    /**
     * Returns the message for this operation's problems dialog.
     *
     * @return the problems message
     */
    protected String getProblemsMessage() {
        return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_problemMessage;
    }

    /**
     * Returns the title for this operation's problems dialog.
     *
     * @return the problems dialog title
     */
    protected String getProblemsTitle() {
        return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyFailedTitle;
    }

    /**
     * Returns whether the source file in a destination collision will be
     * validateEdited together with the collision itself. Returns false. Should
     * return true if the source file is to be deleted after the operation.
     *
     * @return boolean <code>true</code> if the source file in a destination
     * collision should be validateEdited. <code>false</code> if only
     * the destination should be validated.
     */
    protected boolean getValidateConflictSource() {
        return false;
    }

    /**
     * Returns whether the given resources are either both linked or both
     * unlinked.
     *
     * @param source
     * source resource
     * @param destination
     * destination resource
     * @return boolean <code>true</code> if both resources are either linked
     * or unlinked. <code>false</code> otherwise.
     */
    protected boolean homogenousResources(IResource source,
            IResource destination) {
        boolean isSourceLinked = source.isLinked();
        boolean isDestinationLinked = destination.isLinked();

        return (isSourceLinked && isDestinationLinked || isSourceLinked == false
                && isDestinationLinked == false);
    }

    /**
     * Returns whether the given resource is accessible. Files and folders are
     * always considered accessible and a project is accessible if it is open.
     *
     * @param resource
     * the resource
     * @return <code>true</code> if the resource is accessible, and
     * <code>false</code> if it is not
     */
    private boolean isAccessible(IResource resource) {
        switch (resource.getType()) {
        case IResource.FILE:
            return true;
        case IResource.FOLDER:
            return true;
        case IResource.PROJECT:
            return ((IProject) resource).isOpen();
        default:
            return false;
        }
    }

    /**
     * Returns whether any of the given source resources are being recopied to
     * their current container.
     *
     * @param sourceResources
     * the source resources
     * @param destination
     * the destination container
     * @return <code>true</code> if at least one of the given source
     * resource's parent container is the same as the destination
     */
    boolean isDestinationSameAsSource(IResource[] sourceResources,
            IContainer destination) {
        IPath destinationLocation = destination.getLocation();

        for (int i = 0; i < sourceResources.length; i++) {
            IResource sourceResource = sourceResources[i];
            if (sourceResource.getParent().equals(destination)) {
                return true;
            } else if (destinationLocation != null) {
                // do thorough check to catch linked resources. Fixes bug 29913.
 IPath sourceLocation = sourceResource.getLocation();
                IPath destinationResource = destinationLocation
                        .append(sourceResource.getName());
                if (sourceLocation != null
                        && sourceLocation.isPrefixOf(destinationResource)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Copies the given resources to the destination container with the given
     * name.
     * <p>
     * Note: the destination container may need to be created prior to copying
     * the resources.
     * </p>
     *
     * @param resources
     * the resources to copy
     * @param destination
     * the path of the destination container
     * @param monitor
     * a progress monitor for showing progress and for cancelation
     * @return <code>true</code> if the copy operation completed without
     * errors
     */
    private boolean performCopy(IResource[] resources, IPath destination,
            IProgressMonitor monitor) {
        try {
            AbstractWorkspaceOperation op = getUndoableCopyOrMoveOperation(
                    resources, destination);
            op.setModelProviderIds(getModelProviderIds());
            PlatformUI.getWorkbench().getOperationSupport()
                    .getOperationHistory().execute(op, monitor,
                            WorkspaceUndoUtil.getUIInfoAdapter(messageShell));
        } catch (ExecutionException e) {
            if (e.getCause() instanceof CoreException) {
                recordError((CoreException) e.getCause());
            } else {
                IDEWorkbenchPlugin.log(e.getMessage(), e);
                displayError(e.getMessage());
            }
            return false;
        }
        return true;
    }

    /**
     * Individually copies the given resources to the specified destination
     * container checking for name collisions. If a collision is detected, it is
     * saved with a new name.
     * <p>
     * Note: the destination container may need to be created prior to copying
     * the resources.
     * </p>
     *
     * @param resources
     * the resources to copy
     * @param destination
     * the path of the destination container
     * @return <code>true</code> if the copy operation completed without
     * errors.
     */
    private boolean performCopyWithAutoRename(IResource[] resources,
            IPath destination, IProgressMonitor monitor) {
        IWorkspace workspace = resources[0].getWorkspace();
        IPath[] destinationPaths = new IPath[resources.length];
        try {
            for (int i = 0; i < resources.length; i++) {
                IResource source = resources[i];
                destinationPaths[i] = destination.append(source.getName());

                if (workspace.getRoot().exists(destinationPaths[i])) {
                    destinationPaths[i] = getNewNameFor(destinationPaths[i],
                            workspace);
                }
            }
            CopyResourcesOperation op = new CopyResourcesOperation(resources,
                    destinationPaths,
                    IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyTitle);
            op.setModelProviderIds(getModelProviderIds());
            PlatformUI.getWorkbench().getOperationSupport()
                    .getOperationHistory().execute(op, monitor,
                            WorkspaceUndoUtil.getUIInfoAdapter(messageShell));
        } catch (ExecutionException e) {
            if (e.getCause() instanceof CoreException) {
                recordError((CoreException) e.getCause());
            } else {
                IDEWorkbenchPlugin.log(e.getMessage(), e);
                displayError(e.getMessage());
            }
            return false;
        }
        return true;
    }

    /**
     * Performs an import of the given stores into the provided container.
     * Returns a status indicating if the import was successful.
     *
     * @param stores
     * stores that are to be imported
     * @param target
     * container to which the import will be done
     * @param monitor
     * a progress monitor for showing progress and for cancelation
     */
    private void performFileImport(IFileStore[] stores, IContainer target,
            IProgressMonitor monitor) {
        IOverwriteQuery query = new IOverwriteQuery() {
            public String queryOverwrite(String pathString) {
                if (alwaysOverwrite) {
                    return ALL;
                }

                final String returnCode[] = { CANCEL };
                final String msg = NLS
                        .bind(
                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteQuestion,
                                pathString);
                final String [] options = { IDialogConstants.YES_LABEL,
                        IDialogConstants.YES_TO_ALL_LABEL,
                        IDialogConstants.NO_LABEL,
                        IDialogConstants.CANCEL_LABEL };
                messageShell.getDisplay().syncExec(new Runnable () {
                    public void run() {
                        MessageDialog dialog = new MessageDialog(
                                messageShell,
                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_question,
                                null, msg, MessageDialog.QUESTION, options, 0);
                        dialog.open();
                        int returnVal = dialog.getReturnCode();
                        String [] returnCodes = { YES, ALL, NO, CANCEL };
                        returnCode[0] = returnVal == -1 ? CANCEL
                                : returnCodes[returnVal];
                    }
                });
                if (returnCode[0] == ALL) {
                    alwaysOverwrite = true;
                } else if (returnCode[0] == CANCEL) {
                    canceled = true;
                }
                return returnCode[0];
            }
        };

        ImportOperation op = new ImportOperation(target.getFullPath(),
                stores[0].getParent(), FileStoreStructureProvider.INSTANCE,
                query, Arrays.asList(stores));
        op.setContext(messageShell);
        op.setCreateContainerStructure(false);
        try {
            op.run(monitor);
        } catch (InterruptedException e) {
            return;
        } catch (InvocationTargetException e) {
            if (e.getTargetException() instanceof CoreException) {
                displayError(((CoreException) e.getTargetException())
                        .getStatus());
            } else {
                display(e);
            }
            return;
        }
        // Special case since ImportOperation doesn't throw a CoreException on
 // failure.
 IStatus status = op.getStatus();
        if (!status.isOK()) {
            if (errorStatus == null) {
                errorStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
                        IStatus.ERROR, getProblemsMessage(), null);
            }
            errorStatus.merge(status);
        }
    }

    /**
     * Records the core exception to be displayed to the user once the action is
     * finished.
     *
     * @param error
     * a <code>CoreException</code>
     */
    private void recordError(CoreException error) {
        if (errorStatus == null) {
            errorStatus = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.ERROR,
                    getProblemsMessage(), error);
        }

        errorStatus.merge(error.getStatus());
    }

    /**
     * Checks whether the destination is valid for copying the source resources.
     * <p>
     * Note this method is for internal use only. It is not API.
     * </p>
     *
     * @param destination
     * the destination container
     * @param sourceResources
     * the source resources
     * @return an error message, or <code>null</code> if the path is valid
     */
    public String validateDestination(IContainer destination,
            IResource[] sourceResources) {
        if (!isAccessible(destination)) {
            return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationAccessError;
        }
        IContainer firstParent = null;
        URI destinationLocation = destination.getLocationURI();
        for (int i = 0; i < sourceResources.length; i++) {
            IResource sourceResource = sourceResources[i];
            if (firstParent == null) {
                firstParent = sourceResource.getParent();
            } else if (firstParent.equals(sourceResource.getParent()) == false) {
                // Resources must have common parent. Fixes bug 33398.
 return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_parentNotEqual;
            }

            URI sourceLocation = sourceResource.getLocationURI();
            if (sourceLocation == null) {
                if (sourceResource.isLinked()) {
                    // Don't allow copying linked resources with undefined path
 // variables. See bug 28754.
 return NLS
                            .bind(
                                    IDEWorkbenchMessages.CopyFilesAndFoldersOperation_missingPathVariable,
                                    sourceResource.getName());
                }
                return NLS
                        .bind(
                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
                                sourceResource.getName());

            }
            if (sourceLocation.equals(destinationLocation)) {
                return NLS
                        .bind(
                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sameSourceAndDest,
                                sourceResource.getName());
            }
            // is the source a parent of the destination?
 if (new Path(sourceLocation.toString()).isPrefixOf(new Path(
                    destinationLocation.toString()))) {
                return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationDescendentError;
            }

            String linkedResourceMessage = validateLinkedResource(destination,
                    sourceResource);
            if (linkedResourceMessage != null) {
                return linkedResourceMessage;
            }
        }
        return null;
    }

    /**
     * Validates that the given source resources can be copied to the
     * destination as decided by the VCM provider.
     *
     * @param destination
     * copy destination
     * @param sourceResources
     * source resources
     * @return <code>true</code> all files passed validation or there were no
     * files to validate. <code>false</code> one or more files did not
     * pass validation.
     */
    private boolean validateEdit(IContainer destination,
            IResource[] sourceResources) {
        ArrayList copyFiles = new ArrayList ();

        collectExistingReadonlyFiles(destination.getFullPath(),
                sourceResources, copyFiles);
        if (copyFiles.size() > 0) {
            IFile[] files = (IFile[]) copyFiles.toArray(new IFile[copyFiles
                    .size()]);
            IWorkspace workspace = ResourcesPlugin.getWorkspace();
            IStatus status = workspace.validateEdit(files, messageShell);

            canceled = status.isOK() == false;
            return status.isOK();
        }
        return true;
    }

    /**
     * Checks whether the destination is valid for copying the source files.
     * <p>
     * Note this method is for internal use only. It is not API.
     * </p>
     *
     * @param destination
     * the destination container
     * @param sourceNames
     * the source file names
     * @return an error message, or <code>null</code> if the path is valid
     */
    public String validateImportDestination(IContainer destination,
            String [] sourceNames) {

        IFileStore[] stores = new IFileStore[sourceNames.length];
        for (int i = 0; i < sourceNames.length; i++) {
            IFileStore store = IDEResourceInfoUtils
                    .getFileStore(sourceNames[i]);
            if (store == null) {
                return NLS
                        .bind(
                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_infoNotFound,
                                sourceNames[i]);
            }
            stores[i] = store;
        }
        return validateImportDestinationInternal(destination, stores);

    }

    /**
     * Checks whether the destination is valid for copying the source file
     * stores.
     * <p>
     * Note this method is for internal use only. It is not API.
     * </p>
     * <p>
     * TODO Bug 117804. This method has been renamed to avoid a bug in the
     * Eclipse compiler with regards to visibility and type resolution when
     * linking.
     * </p>
     *
     * @param destination
     * the destination container
     * @param sourceStores
     * the source IFileStore
     * @return an error message, or <code>null</code> if the path is valid
     */
    private String validateImportDestinationInternal(IContainer destination,
            IFileStore[] sourceStores) {
        if (!isAccessible(destination))
            return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationAccessError;

        IFileStore destinationStore;
        try {
            destinationStore = EFS.getStore(destination.getLocationURI());
        } catch (CoreException exception) {
            IDEWorkbenchPlugin.log(exception.getLocalizedMessage(), exception);
            return NLS
                    .bind(
                            IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
                            exception.getLocalizedMessage());
        }
        for (int i = 0; i < sourceStores.length; i++) {
            IFileStore sourceStore = sourceStores[i];
            IFileStore sourceParentStore = sourceStore.getParent();

            if (sourceStore != null) {
                if (destinationStore.equals(sourceStore)
                        || (sourceParentStore != null && destinationStore
                                .equals(sourceParentStore))) {
                    return NLS
                            .bind(
                                    IDEWorkbenchMessages.CopyFilesAndFoldersOperation_importSameSourceAndDest,
                                    sourceStore.getName());
                }
                // work around bug 16202. replacement for
 // sourcePath.isPrefixOf(destinationPath)
 IFileStore destinationParent = destinationStore.getParent();
                if (sourceStore.isParentOf(destinationParent)) {
                    return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationDescendentError;
                }

            }
        }
        return null;
    }

    /**
     * Check if the destination is valid for the given source resource.
     *
     * @param destination
     * destination container of the operation
     * @param source
     * source resource
     * @return String error message or null if the destination is valid
     */
    private String validateLinkedResource(IContainer destination,
            IResource source) {
        if (source.isLinked() == false) {
            return null;
        }
        IWorkspace workspace = destination.getWorkspace();
        IResource linkHandle = createLinkedResourceHandle(destination, source);
        IStatus locationStatus = workspace.validateLinkLocation(linkHandle,
                source.getRawLocation());

        if (locationStatus.getSeverity() == IStatus.ERROR) {
            return locationStatus.getMessage();
        }
        IPath sourceLocation = source.getLocation();
        if (source.getProject().equals(destination.getProject()) == false
                && source.getType() == IResource.FOLDER
                && sourceLocation != null) {
            // prevent merging linked folders that point to the same
 // file system folder
 try {
                IResource[] members = destination.members();
                for (int j = 0; j < members.length; j++) {
                    if (sourceLocation.equals(members[j].getLocation())
                            && source.getName().equals(members[j].getName())) {
                        return NLS
                                .bind(
                                        IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sameSourceAndDest,
                                        source.getName());
                    }
                }
            } catch (CoreException exception) {
                displayError(NLS
                        .bind(
                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
                                exception.getMessage()));
            }
        }
        return null;
    }

    /**
     * Returns whether moving all of the given source resources to the given
     * destination container could be done without causing name collisions.
     *
     * @param destination
     * the destination container
     * @param sourceResources
     * the list of resources
     * @return <code>true</code> if there would be no name collisions, and
     * <code>false</code> if there would
     */
    private IResource[] validateNoNameCollisions(IContainer destination,
            IResource[] sourceResources) {
        List copyItems = new ArrayList ();
        IWorkspaceRoot workspaceRoot = destination.getWorkspace().getRoot();
        int overwrite = IDialogConstants.NO_ID;

        // Check to see if we would be overwriting a parent folder.
 // Cancel entire copy operation if we do.
 for (int i = 0; i < sourceResources.length; i++) {
            final IResource sourceResource = sourceResources[i];
            final IPath destinationPath = destination.getFullPath().append(
                    sourceResource.getName());
            final IPath sourcePath = sourceResource.getFullPath();

            IResource newResource = workspaceRoot.findMember(destinationPath);
            if (newResource != null && destinationPath.isPrefixOf(sourcePath)) {
                displayError(NLS
                        .bind(
                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteProblem,
                                destinationPath, sourcePath));

                canceled = true;
                return null;
            }
        }
        // Check for overwrite conflicts
 for (int i = 0; i < sourceResources.length; i++) {
            final IResource source = sourceResources[i];
            final IPath destinationPath = destination.getFullPath().append(
                    source.getName());

            IResource newResource = workspaceRoot.findMember(destinationPath);
            if (newResource != null) {
                if (overwrite != IDialogConstants.YES_TO_ALL_ID
                        || (newResource.getType() == IResource.FOLDER && homogenousResources(
                                source, destination) == false)) {
                    overwrite = checkOverwrite(source, newResource);
                }
                if (overwrite == IDialogConstants.YES_ID
                        || overwrite == IDialogConstants.YES_TO_ALL_ID) {
                    copyItems.add(source);
                } else if (overwrite == IDialogConstants.CANCEL_ID) {
                    canceled = true;
                    return null;
                }
            } else {
                copyItems.add(source);
            }
        }
        return (IResource[]) copyItems.toArray(new IResource[copyItems.size()]);
    }

    private void copyResources(final IResource[] resources,
            final IPath destinationPath, final IResource[][] copiedResources,
            IProgressMonitor monitor) {
        IResource[] copyResources = resources;

        // Fix for bug 31116. Do not provide a task name when
 // creating the task.
 monitor.beginTask("", 100); //$NON-NLS-1$
 monitor.setTaskName(getOperationTitle());
        monitor.worked(10); // show some initial progress

        // Checks only required if this is an exisiting container path.
 boolean copyWithAutoRename = false;
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        if (root.exists(destinationPath)) {
            IContainer container = (IContainer) root
                    .findMember(destinationPath);
            // If we're copying to the source container then perform
 // auto-renames on all resources to avoid name collisions.
 if (isDestinationSameAsSource(copyResources, container)
                    && canPerformAutoRename()) {
                copyWithAutoRename = true;
            } else {
                // If no auto-renaming will be happening, check for
 // potential name collisions at the target resource
 copyResources = validateNoNameCollisions(container,
                        copyResources);
                if (copyResources == null) {
                    if (canceled) {
                        return;
                    }
                    displayError(IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameCollision);
                    return;
                }
                if (validateEdit(container, copyResources) == false) {
                    return;
                }
            }
        }

        errorStatus = null;
        if (copyResources.length > 0) {
            if (copyWithAutoRename) {
                performCopyWithAutoRename(copyResources, destinationPath,
                        new SubProgressMonitor(monitor, 90));
            } else {
                performCopy(copyResources, destinationPath, new SubProgressMonitor(monitor, 90));
            }
        }
        monitor.done();
        copiedResources[0] = copyResources;
    }

    private void copyFileStores(final IFileStore[] stores,
            final IPath destinationPath, IProgressMonitor monitor) {
        // Checks only required if this is an exisiting container path.
 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        if (root.exists(destinationPath)) {
            IContainer container = (IContainer) root
                    .findMember(destinationPath);

            performFileImport(stores, container, monitor);
        }
    }

    /**
     * Returns the model provider ids that are known to the client that
     * instantiated this operation.
     *
     * @return the model provider ids that are known to the client that
     * instantiated this operation.
     * @since 3.2
     */
    public String [] getModelProviderIds() {
        return modelProviderIds;
    }

    /**
     * Sets the model provider ids that are known to the client that
     * instantiated this operation. Any potential side effects reported by these
     * models during validation will be ignored.
     *
     * @param modelProviderIds
     * the model providers known to the client who is using this
     * operation.
     * @since 3.2
     */
    public void setModelProviderIds(String [] modelProviderIds) {
        this.modelProviderIds = modelProviderIds;
    }

    /**
     * Returns an AbstractWorkspaceOperation suitable for performing the move or
     * copy operation that will move or copy the given resources to the given
     * destination path.
     *
     * @param resources
     * the resources to be moved or copied
     * @param destinationPath
     * the destination path to which the resources should be moved
     * @return the operation that should be used to perform the move or cop
     * @since 3.3
     */
    protected AbstractWorkspaceOperation getUndoableCopyOrMoveOperation(
            IResource[] resources, IPath destinationPath) {
        return new CopyResourcesOperation(resources, destinationPath,
                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyTitle);

    }
}

